package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type User struct {
	gorm.Model
	Name   string `gorm:"default:'原立普'"`
	Age    int
	Gender string `json:"gender"` //灵活使用tag做个性化处理
}

func main() {
	//creatTable()
	ormCrud()
	return
	// 创建一个默认的路由引擎
	r := gin.Default()
	// 模板解析
	// 加载一个文件
	//r.LoadHTMLFiles("templates/user/index.tmpl")
	// 加载多个文件
	r.LoadHTMLGlob("templates/**/*")
	// 加载静态文件
	r.Static("/static", "./static")
	// 全局注册中间件
	//r.Use(middleware1, middleware2)
	// 为单个请求注册注册中间件
	r.GET("/hello", middleware2, hello)
	//r.GET("/hello", hello)
	r.GET("/routeRedirect", func(c *gin.Context) {
		// 指定重定向的URL
		c.Request.URL.Path = "/hello"
		r.HandleContext(c)
	})
	r.GET("/gotoBaidu", gotoBaidu)
	r.GET("/student", student)
	r.GET("/admin/index", adminIndex)
	r.POST("/login", login)
	r.POST("/json", get_json)
	r.GET("/user/search/:username/:address", get_path)
	// 单个路由
	r.GET("/user/index", middleware2, userIndex)
	//r.GET("/user/upload", upload)
	//r.POST("/user/upload", uploadPost)
	// 可以匹配所有请求方法的any方法
	r.Any("/user/upload", func(c *gin.Context) {
		switch c.Request.Method {
		case "GET":
			c.HTML(http.StatusOK, "user/upload.tmpl", "ok")
		case http.MethodPost:
			file, err := c.FormFile("f1")
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{
					"message": err.Error(),
				})
				return
			}

			log.Println(file.Filename)
			dst := fmt.Sprintf("./%s", file.Filename)
			fmt.Println(dst)
			// 上传文件到指定的目录
			c.SaveUploadedFile(file, dst)
			c.JSON(http.StatusOK, gin.H{
				"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
			})
		}
	})
	// 路由组
	// 我们可以将拥有共同URL前缀的路由划分为一个路由组。习惯性一对{}包裹同组的路由，这只是为了看着清晰，你用不用{}包裹功能上没什么区别。
	userGroup := r.Group("/user", middleware1) //为路由组注册中间件
	//userGroup := r.Group("/user")
	{
		userGroup.GET("/multiUpload", multiUpload)
		userGroup.POST("/multiUpload", multiUploadPost)
		// 嵌套路由组
		xx := userGroup.Group("xx")
		xx.GET("/oo", hello)
	}
	// 404
	// 为没有配置处理函数的路由添加处理程序，默认情况下它返回404代码，下面的代码为没有匹配到路由的请求都返回
	r.NoRoute(func(c *gin.Context) {
		c.JSON(200, gin.H{
			"value": "not found",
		})
		// user/404.html" is undefined
		//c.HTML(http.StatusNotFound, "user/404.html", nil)
	})
	// 启动HTTP服务，默认在0.0.0.0:8080启动服务
	r.Run()
}

func creatTable() {
	// 数据表前缀
	// gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
	// 	return "prefix_" + defaultTableName
	// }
	// 打开数据库
	db, err := gorm.Open("mysql", "root:password@(localhost)/mydb?charset=utf8mb4&parseTime=True&loc=Local")
	if err != nil {
		panic(err)
	}
	defer db.Close()
	// 禁用表名复数
	db.SingularTable(true)
	//  自动迁移 （把结构体和数据表进行对应）
	db.AutoMigrate(&User{})
	// 创建表插入一条记录
	u1 := User{Name: "yuan", Age: 23, Gender: "male"}
	u2 := User{Name: "jinzhu", Age: 23, Gender: "male"}
	db.NewRecord(&u1) //判断主键是否为空 是不是一条新的数据
	//db.Debug().Create(&u1)// 打印出sql语句
	db.Create(&u1)
	db.Create(&u2)
	//db.Table("yuanlp").CreateTable(&User{})
	// 查询
	var u User
	db.First(&u) // 查询第一条数据
	//fmt.Printf("u:%#v\n", u) //u:main.User{Id:1, Name:"yuan", Age:23, Gender:"male"}
	// 更新
	//db.Model(&u).Update("Age", 18)
	// 删除
	//db.Delete(&u)

}

func ormCrud() {
	// 打开数据库
	db, err := gorm.Open("mysql", "root:password@(localhost)/mydb?charset=utf8mb4&parseTime=True&loc=Local")
	if err != nil {
		panic(err)
	}
	defer db.Close()
	db.SingularTable(true)
	var user []User
	// 根据主键查询第一条记录
	//db.First(&user)
	//// SELECT * FROM users ORDER BY id LIMIT 1;

	// 随机获取一条记录
	//db.Take(&user)
	//// SELECT * FROM users LIMIT 1;

	// 根据主键查询最后一条记录
	//db.Last(&user)
	//// SELECT * FROM users ORDER BY id DESC LIMIT 1;

	// 查询所有的记录
	//db.Find(&user)
	//// SELECT * FROM users;
	// where 条件查询
	// 查询指定的某条记录(仅当主键为整型时可用)
	//db.First(&user, 2)
	//// SELECT * FROM users WHERE id = 2;

	// Get first matched record
	db.Where("name = ?", "jinzhu").First(&user)
	//// SELECT * FROM users WHERE name = 'jinzhu' limit 1;

	// Get all matched records
	db.Debug().Where("name = ?", "jinzhu").Find(&user)
	//// SELECT * FROM users WHERE name = 'jinzhu';

	// <>
	db.Where("name <> ?", "jinzhu").Find(&user)
	//// SELECT * FROM users WHERE name <> 'jinzhu';

	// IN
	db.Where("name IN (?)", []string{"jinzhu", "jinzhu 2"}).Find(&user)
	//// SELECT * FROM users WHERE name in ('jinzhu','jinzhu 2');

	// LIKE
	db.Where("name LIKE ?", "%jin%").Find(&user)
	//// SELECT * FROM users WHERE name LIKE '%jin%';

	// AND
	db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&user)
	//// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;

	// Time
	//db.Where("updated_at > ?", lastWeek).Find(&user)
	//// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';

	// BETWEEN
	//db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&user)
	//// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

	// Struct
	db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
	//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;

	// Map
	db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&user)
	//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;

	// 主键的切片
	db.Where([]int64{20, 21, 22}).Find(&user)
	//// SELECT * FROM users WHERE id IN (20, 21, 22);

	// Not 条件
	db.Not("name", "jinzhu").First(&user)
	//// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;

	// Not In
	db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&user)
	//// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");

	// Not In slice of primary keys
	db.Not([]int64{1, 2, 3}).First(&user)
	//// SELECT * FROM users WHERE id NOT IN (1,2,3);

	db.Not([]int64{}).First(&user)
	//// SELECT * FROM users;

	// Plain SQL
	db.Not("name = ?", "jinzhu").First(&user)
	//// SELECT * FROM users WHERE NOT(name = "jinzhu");

	// Struct
	db.Not(User{Name: "jinzhu"}).First(&user)
	//// SELECT * FROM users WHERE name <> "jinzhu";

	//Or条件
	db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&user)
	//// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';

	// Struct
	db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&user)
	//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';

	// Map
	db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&user)
	//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';
}

// 输出json
func hello(c *gin.Context) {
	c.JSON(200, gin.H{
		"method": "hello",
	})
}

// 渲染html并传输数据
func userIndex(c *gin.Context) {
	user1 := User{
		Name:   "yuan",
		Age:    20,
		Gender: "man",
	}
	// 从上下文中取值
	fmt.Println(c.Get("name"))
	// 模板渲染
	c.HTML(http.StatusOK, "user/index.tmpl", gin.H{
		"title": "user_index",
		"Name":  user1.Name,
		"Age":   user1.Age,
	})
}

func adminIndex(c *gin.Context) {
	user1 := User{
		Name:   "wei",
		Age:    12,
		Gender: "woman",
	}
	// 模板渲染
	c.HTML(http.StatusOK, "admin/index.tmpl", gin.H{
		"title": "admin_index",
		"Name":  user1.Name,
		"Age":   user1.Age,
	})
}

// 获取get传入的query string
func student(c *gin.Context) {
	// student?name=yuan
	//name := c.Query("name")
	//name := c.DefaultQuery("name", "mike")
	// 附加返回一个bool返回值
	name, _ := c.GetQuery("name")
	c.JSON(200, gin.H{
		"name": name,
	})
}

//获取post form
func login(c *gin.Context) {
	//name := c.PostForm("name")//取不到值返回空字符串
	//pwd := c.PostForm("pwd")
	//name := c.DefaultPostForm("name", "someone")
	//pwd := c.DefaultPostForm("pwd", "123")
	name, _ := c.GetPostForm("name") //取不到_=false
	pwd, _ := c.GetPostForm("pwd")
	c.JSON(200, gin.H{
		"name": name,
		"pwd":  pwd,
	})
}

// 获取json参数
func get_json(c *gin.Context) {
	b, _ := c.GetRawData() // 从c.Request.Body读取请求数据
	// 定义map或结构体
	var m map[string]interface{}
	// 反序列化
	_ = json.Unmarshal(b, &m)
	c.JSON(http.StatusOK, m)
}

// 获取path参数
func get_path(c *gin.Context) {
	// /user/search/:username/:address"
	username := c.Param("username")
	address := c.Param("address")
	c.JSON(200, gin.H{
		"username": username,
		"address":  address,
	})
}

type Login struct {
	User     string `form:"user" json:"user" binding:"required"`
	Password string `form:"password" json:"password" binding:"required"`
}

// 参数绑定
func bind_test(c *gin.Context) {
	var login Login
	if err := c.ShouldBind(&login); err == nil {
		fmt.Printf("login info:%#v\n", login)
		c.JSON(http.StatusOK, gin.H{
			"user":     login.User,
			"password": login.Password,
		})
	} else {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	}
}

// 单个文件上传页面
func upload(c *gin.Context) {
	c.HTML(http.StatusOK, "user/upload.tmpl", "ok")
}

// 单个文件上传方法
func uploadPost(c *gin.Context) {
	file, err := c.FormFile("f1")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"message": err.Error(),
		})
		return
	}

	log.Println(file.Filename)
	dst := fmt.Sprintf("./%s", file.Filename)
	fmt.Println(dst)
	// 上传文件到指定的目录
	c.SaveUploadedFile(file, dst)
	c.JSON(http.StatusOK, gin.H{
		"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
	})
}

// 多个文件上传页面
func multiUpload(c *gin.Context) {
	c.HTML(http.StatusOK, "user/multiUpload.tmpl", "ok")
}

// 多个文件上传
func multiUploadPost(c *gin.Context) {
	form, _ := c.MultipartForm()
	files := form.File["file"]

	for index, file := range files {
		log.Println(file.Filename)
		dst := fmt.Sprintf("./%s_%d", file.Filename, index)
		// 上传文件到指定的目录
		c.SaveUploadedFile(file, dst)
	}
	c.JSON(http.StatusOK, gin.H{
		"message": fmt.Sprintf("%d files uploaded!", len(files)),
	})
}

// http重定向
func gotoBaidu(c *gin.Context) {
	c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
}

// 中间件
func middleware1(c *gin.Context) {
	fmt.Println("m1 in")
	//计时
	start := time.Now()
	c.Next()
	cost := time.Since(start)
	fmt.Printf("cost:%v\n", cost)
	fmt.Println("m1 out")
}

// 中间件2
func middleware2(c *gin.Context) {
	fmt.Println("m2 in")
	c.Set("name", "weisuying")
	c.Next()
	//c.Abort() //阻止后续
	fmt.Println("m2 out")
}

func outMiddleware(doCheck bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		if doCheck {

		} else {
			c.Next()
		}
	}
}
